/***********************************
 *
 * RRDJTOOL
 * JNI Wrapper for RRDTool functions
 * version 1.0.1 (Feb 17, 2003)
 * by Sasa Markovic <sasam@dnseurope.co.uk>
 * 
 * version 1.0.2 (Nov 17, 2006)
 * by Roberto Polli <rpolli@babel.it>
 *
 * Currently supported functions:
 *
 *		rrdcreate
 *      rrdgraph
 *		rrdupdate (fixed bug in initial release)
 *      rrdfetch
 *      rrdlast
 *      rrdxport
 */

#include "Rrd.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <rrd.h>

#include <rrd_xport.h>
#include <rrd_nan_inf.c>

extern int optind;
extern int opterr;

// last error is here...
static char* lastError = 0;

// last graph output is here
static char* lastGraphOutput = 0;

// last fetch data is here
static unsigned long lastStart = 0, lastEnd = 0, lastStep = 0;
static int lastDsCnt = 0;
static char **lastDsNames = 0;
static rrd_value_t *lastData = 0;


////////////////////////////////////////////////////

void reset() {
	optind=0; /* reset gnu getopt */
    opterr=0; /* no error messages */
}

char *duplicate(const char *source) {
	char *buffer = (char *) malloc(strlen(source) + 1);
	strcpy(buffer, source);
	return buffer;
}

char **getTokens(JNIEnv *env, jobjectArray ar, int n) {
	char **tokens = (char **)malloc(n * sizeof(char*));
	int i;
	reset();
	for(i = 0; i < n; i++) {
		jstring s = (jstring)((*env)->GetObjectArrayElement(env, ar, i));
		const char *str = (*env)->GetStringUTFChars(env, s, 0);
		tokens[i] = duplicate(str);
		(*env)->ReleaseStringUTFChars(env, s, str);
	}
	return tokens;
}

void preserveError() {
	free(lastError);
	if (rrd_test_error()) {
        lastError = duplicate(rrd_get_error());
        rrd_clear_error();
    }
    else {
    	lastError = duplicate("");
    }
}

void preserveGraphOutput(char **calcpr, int xsize, int ysize) {
	free(lastGraphOutput);
	if(calcpr) {
		int i;
		lastGraphOutput = (char *)malloc(2000);
		sprintf(lastGraphOutput, "%dx%d\n", xsize, ysize);
		for(i=0; calcpr[i]; i++){
			strcat(lastGraphOutput, calcpr[i]);
			strcat(lastGraphOutput, "\n");
			free(calcpr[i]);
		}
		free(calcpr);
	}
	else {
		lastGraphOutput = duplicate("");
	}
}

void releaseTokens(char **tokens, int n) {
	int i;
	for(i = 0; i < n; i++) {
		free(tokens[i]);
	}
	free(tokens);
}

void preserveFetchOutput(time_t start, time_t end, unsigned long step,
	unsigned long ds_cnt, char **ds_namv, rrd_value_t *data) {
	// free old preserved values
	int i;
	for(i = 0; i < lastDsCnt; i++) {
		free(lastDsNames[i]);
	}
	free(lastDsNames);
	free(lastData);
	// save new values
	lastStart = start;
	lastEnd = end;
	lastStep = step;
	lastDsCnt = ds_cnt;
	lastDsNames = ds_namv;
	lastData = data;
}

// JNI Functions

JNIEXPORT jint JNICALL Java_rrd_Rrd_createRrdDatabase
	(JNIEnv *env, jobject obj, jobjectArray ar) {
	int n = (*env)->GetArrayLength(env, ar);
	char **tokens = getTokens(env, ar, n);
	int status = rrd_create(n, tokens);
	preserveError();
	releaseTokens(tokens, n);
	return status;
}

JNIEXPORT jint JNICALL Java_rrd_Rrd_updateRrdDatabase
	(JNIEnv *env, jobject obj, jobjectArray ar) {
	int n = (*env)->GetArrayLength(env, ar);
	char **tokens = getTokens(env, ar, n);
	int status = rrd_update(n, tokens);
	preserveError();
	releaseTokens(tokens, n);
	return status;
}
JNIEXPORT jint JNICALL Java_rrd_Rrd_createRrdGraph
	(JNIEnv *env, jobject obj, jobjectArray ar) {
	int n = (*env)->GetArrayLength(env, ar);
	char **tokens = getTokens(env, ar, n);
	char **calcpr;
	int xsize, ysize;
	double ymin,ymax;
	int status = rrd_graph(n, tokens, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
	preserveError();
	if(status != -1) {
		preserveGraphOutput(calcpr, xsize, ysize);
	}
	releaseTokens(tokens, n);
	return status;
}
JNIEXPORT jlong JNICALL Java_rrd_Rrd_getRrdLast
	(JNIEnv *env, jobject obj, jobjectArray ar) {
	int n = (*env)->GetArrayLength(env, ar);
	char **tokens = getTokens(env, ar, n);
	time_t last = rrd_last(n, tokens);
	preserveError();
	releaseTokens(tokens, n);
	return last;
}

JNIEXPORT jint JNICALL Java_rrd_Rrd_fetchRrdDatabase
	(JNIEnv *env, jobject obj, jobjectArray ar) {
	int n = (*env)->GetArrayLength(env, ar);
	char **tokens = getTokens(env, ar, n);
	time_t start, end;
	unsigned long step, ds_cnt;
	rrd_value_t *data;
	char **ds_namv;
	int status = rrd_fetch(n, tokens, &start, &end, &step, &ds_cnt, &ds_namv, &data);
	preserveError();
	if(status != -1) {
		preserveFetchOutput(start, end, step, ds_cnt, ds_namv, data);
	}
	releaseTokens(tokens, n);
	return status;
}

/** \todo ricreare la funzione Xport */
JNIEXPORT jint JNICALL Java_rrd_Rrd_xportRrdDatabase
	(JNIEnv *env, jobject obj, jobjectArray ar) {
	int n = (*env)->GetArrayLength(env, ar);
	char **tokens = getTokens(env, ar, n);
	time_t start, end;
	unsigned long step;
	rrd_value_t *data;

	rrd_value_t *ptr; //
	time_t  ti;
	char          **legend_v;
	unsigned long    col_cnt,row_cnt;
	int xxsize;
	
	unsigned long int j = 0;
	int status = rrd_xport(n , tokens , &xxsize,&start,&end,&step,&col_cnt,&legend_v,&data) ;
        if( status != -1) {
          row_cnt = (end-start)/step;
          ptr = data;
          printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING);
          printf("<%s>\n", ROOT_TAG);
          printf("  <%s>\n", META_TAG);
          printf("    <%s>%lu</%s>\n", META_START_TAG, start+step, META_START_TAG);
          printf("    <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
          printf("    <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
          printf("    <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG);
          printf("    <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG);
          printf("    <%s>\n", LEGEND_TAG);
          for (j = 0; j < col_cnt; j++) {
            char *entry = NULL;
            entry = legend_v[j];
            printf("      <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG);
            free(entry);
          }
          free(legend_v);
          printf("    </%s>\n", LEGEND_TAG);
          printf("  </%s>\n", META_TAG);
          printf("  <%s>\n", DATA_TAG);
          for (ti = start+step; ti <= end; ti += step) {
            printf ("    <%s>", DATA_ROW_TAG);
            printf ("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
            for (j = 0; j < col_cnt; j++) {
              rrd_value_t newval = DNAN;
              newval = *ptr;
              if(isnan(newval)){
                printf("<%s>NaN</%s>", COL_DATA_TAG, COL_DATA_TAG);
              } else {
                printf("<%s>%0.10e</%s>", COL_DATA_TAG, newval, COL_DATA_TAG);
              };
              ptr++;
            }
            printf("</%s>\n", DATA_ROW_TAG);
          }
          free(data);
          printf("  </%s>\n", DATA_TAG);
          printf("</%s>\n", ROOT_TAG);
        }


	/* fin qui*/

	preserveError();
	if(status != -1) {
// 		free(legend_v);
// 		free(data);
	}
	releaseTokens(tokens, n);
	return status;
}


/*
time_t        start,end;
	unsigned long step, ds_cnt,i,ii;
	rrd_value_t   *data,*datai;
	char          **ds_namv;
	if (rrd_fetch(argc-1, &argv[1],&start,&end,&step,&ds_cnt,&ds_namv,&data) != -1) {
	    datai=data;
	    printf("           ");
	    for (i = 0; i<ds_cnt;i++)
	        printf("%14s",ds_namv[i]);
	    printf ("\n\n");
	    for (i = start; i <= end; i += step){
	        printf("%10lu:", i);
	        for (ii = 0; ii < ds_cnt; ii++)
		    printf(" %0.10e", *(datai++));
	        printf("\n");
	    }
	    for (i=0;i<ds_cnt;i++)
	          free(ds_namv[i]);
	    free(ds_namv);
	    free (data);
	}
*/

JNIEXPORT jstring JNICALL Java_rrd_Rrd_getRrdError
	(JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, duplicate(lastError));
}

JNIEXPORT jstring JNICALL Java_rrd_Rrd_getRrdOutput
	(JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, duplicate(lastGraphOutput));
}

JNIEXPORT jobjectArray JNICALL Java_rrd_Rrd_getDsNames
	(JNIEnv *env, jobject obj) {
    jobjectArray ar = (jobjectArray) ((*env)->NewObjectArray(
    	env, lastDsCnt,
    	(*env)->FindClass(env, "java/lang/String"),
    	(*env)->NewStringUTF(env, "")));
    int i;
    for(i = 0; i < lastDsCnt; i++) {
    	(*env)->SetObjectArrayElement(env, ar, i,
    		(*env)->NewStringUTF(env, duplicate(lastDsNames[i])));
    }
    return ar;
}

JNIEXPORT jlongArray JNICALL Java_rrd_Rrd_getTimestamps
	(JNIEnv *env, jobject obj) {
	jlong vals[3] = { lastStart, lastEnd, lastStep };
    jlongArray ar = (*env)->NewLongArray(env, 3);
    (*env)->SetLongArrayRegion(env, ar, 0, 3, vals);
    return ar;
}

JNIEXPORT jdoubleArray JNICALL Java_rrd_Rrd_getDsValues
	(JNIEnv *env, jobject obj) {
	int n = lastDsCnt * ((lastEnd - lastStart) / lastStep + 1);
	jdoubleArray ar = (*env)->NewLongArray(env, n);
	(*env)->SetDoubleArrayRegion(env, ar, 0, n, lastData);
	return ar;
}
